#include "servicecode/WebPageSearch.h"

#include <climits>

WebPageSearch::WebPageSearch(const string &str)
    : _searchtrems(str)
{
    LogDebug("创建网页搜索对象，进行网页搜索");
}

string &WebPageSearch::getresult()
{
    LogDebug("从库中加载需要的文章");
    int i = 0;
    vector<WebPage> pages;
    // 获取单例并检查有效性
    auto loadWebPage = LoadWebPage::getInstance();
    if (!loadWebPage)
    {
        LogError("网页库未初始化");
        return _json_string;
    }

    // 检查_result有效性
    if (_result.empty())
    {
        LogWarn("搜索结果为空");
        return _json_string;
    }

    for (auto &it : _result)
    {
        LogDebug("从库中加载第 %d 篇文章,文章id:%d", i, it.second);
        if (i >= 20) // 只加载前20篇网页
        {
            break;
        }

        // 检查ID有效性
        int id = it.second;

        WebPage page;
        if (!loadWebPage->LodPagefromLib(id, page))
        {
            LogWarn("加载文章失败, ID: %d", id);
            continue;
        }

        pages.push_back(page);
        ++i;
        LogDebug("success");
    }
    LogDebug("将每个文章放入json中");
    json js;
    js["status"] = "success";
    for (auto &page : pages)
    {
        json j;
        j["title"] = page._title;
        j["url"] = page._link;
        if (!page._description.empty())
        {
            j["snippet"] = safe_utf8_substr(page._description, 1000);
        }
        js["results"].push_back(j);
    }
    _json_string = js.dump();
    return _json_string;
}

bool WebPageSearch::toSearch()
{
    LogInfo("进行网页搜索：%s", _searchtrems.c_str());
    // 检查搜索词是否为空
    if (_searchtrems.empty())
    {
        LogError("收到空搜索词");
        return false;
    }

    // 将接收到的词进行分词
    vector<string> words;
    SplitTool::getInstance()->Splitwords(_searchtrems, words);

    // 检查分词结果
    if (words.empty())
    {
        LogError("分词结果为空");
        return false;
    }

    LogInfo("原始分词结果:");
    for (const auto &w : words)
    {
        LogInfo("- %s", w.c_str());
    }

    map<int, map<string, double>> weights; // 存放余弦相似算法的数据，文章id，单词，权重
    map<string, int> frequents;            // 存放搜索词的频率
    vector<set<int>> ids(words.size());    // 存放出现这些单词的文章id集合
    int i = 0;
    auto loadWebPage = LoadWebPage::getInstance();
    if (!loadWebPage)
    {
        LogError("网页库未成功加载");
        return false;
    }
    // 进行搜索
    for (auto &word : words)
    {
        auto &invertIndex = loadWebPage->_invertIndexLib;
        auto it = invertIndex.find(word);
        ++frequents[word];
        if (it != invertIndex.end())
        {
            for (auto &it : it->second)
            {
                weights[it.first][word] = it.second;
                ids[i].insert(it.first);
            }
            LogInfo("在文章中找到搜索词：%s", word.c_str());
        }
        ++i;
    }
    if (ids.empty())
    {
        LogInfo("没有找到任何文章包含搜索词");
        return false;
    }

    set<int> res = intersect_sets(ids); // 对包含这些单词的文章id取交集
    // 使用显式迭代器
    for (auto it = weights.begin(); it != weights.end();)
    {
        if (res.find(it->first) == res.end())
        {
            it = weights.erase(it); // erase() 返回下一个有效迭代器
        }
        else
        {
            ++it;
        }
    }

    // 计算搜索词的权重
    map<string, double> standard = calculateweights(frequents);
    // 进行文章相关度排序（余弦相似度算法）
    Consinesimilarity(standard, weights);
    return true;
}

// 取每个set集合的交集
set<int> WebPageSearch::intersect_sets(const std::vector<std::set<int>> &sets)
{
    // 处理空容器的情况
    if (sets.empty())
    {
        return std::set<int>();
    }

    // 初始化结果为第一个集合
    std::set<int> result = sets[0];
    std::set<int> temp; // 临时存储中间结果

    // 遍历后续集合（从第二个开始）
    for (size_t i = 1; i < sets.size(); ++i)
    {
        // 清空临时容器
        temp.clear();
        // 计算 result 和 sets[i] 的交集，存入 temp
        std::set_intersection(
            result.begin(), result.end(),
            sets[i].begin(), sets[i].end(),
            std::inserter(temp, temp.begin()));
        // 用移动语义更新 result，避免拷贝开销
        result = std::move(temp);
    }
    ostringstream oss;
    oss << result;
    LogDebug("包含所有搜索词的文章个数:%d", result.size());
    // LogDebug("包含所有搜索词的文章id:%s", oss.str().c_str());
    return result;
}

// 将搜索词看成一篇文章，计算每个单词的权重
map<string, double> WebPageSearch::calculateweights(const map<string, int> &frequents)
{
    map<string, double> weights;
    int N = LoadWebPage::getInstance()->_newoffset.size() + 1;
    int TF;
    int DF = 1;
    double IDF = log2(N / (DF + 1));
    double w;
    for (auto &it : frequents)
    {
        TF = it.second;
        w = TF * IDF;
        weights.insert({it.first, w});
    }
    return weights;
}
// 余弦相似算法
void WebPageSearch::Consinesimilarity(const map<string, double> &standard, const map<int, map<string, double>> &weights)
{
    // 计算基准向量的模长
    double mod = 0;
    for (auto &it : standard)
    {
        mod += pow(it.second, 2);
    }
    mod = sqrt(mod);

    // 计算每个文章向量与基准向量的余弦值
    for (auto &it : weights)
    {
        double mod_w = 0;
        for (auto &it2 : it.second)
        {
            mod_w += pow(it2.second, 2);
        }
        mod_w = sqrt(mod_w); // 文章的向量模长
        double cosine = 0;   // 与基准向量的余弦值
        for (auto &it3 : standard)
        {
            auto weightIt = weights.find(it.first);
            if (weightIt != weights.end())
            {
                auto valIt = weightIt->second.find(it3.first);
                if (valIt != weightIt->second.end())
                {
                    cosine += (standard.at(it3.first) * valIt->second);
                }
            }
        }
        _result.insert({cosine, it.first});
    }

    ostringstream oss;
    for (auto &it : _result)
    {
        oss << it.second << " ";
    }
    LogDebug("根据余弦值对网页排序:%s", oss.str().c_str());
}

// 安全截取UTF8字符串
string WebPageSearch::safe_utf8_substr(const string &str, size_t max_length)
{
    // 如果字符串较短，直接返回
    if (str.length() <= max_length)
    {
        return str;
    }

    // 从指定位置开始向前查找，直到找到完整的字符边界
    size_t end = max_length;

    // 查找字符边界（避免截断多字节字符）
    while (end > 0 && (static_cast<unsigned char>(str[end]) >= 0x80 &&
                       static_cast<unsigned char>(str[end]) <= 0xBF))
    {
        end--;
    }

    // 如果找到边界，截断到该位置
    if (end > 0)
    {
        return str.substr(0, end)+"......";
    }

    // 如果找不到边界（整个字符串都是无效字节），返回空字符串
    return "";
}